/*Software:音乐律动音箱-悦-2.0-专属律动程序
 *Author:大超
 *B站：super大大怪i
 *
 *灯带：144/米
 *特点：音乐律动，24种灯光秀效果
 *
 *程序说明：由于声音的强度在密闭环境和宽广环境不一样，箱体内麦克风采集的声音信
 *号比麦克风外置采集的信号要高，所以该程序适用于密闭箱体中的麦克风信号采集
 *
 *使用设置：需要在无声环境下将麦克风的信号值调至400左右（一般只需要调整此值）
 *         如果觉得麦克风太灵敏，可以将变量filter_scale适当调高
 *         建议在安静状态下启动音箱，启动时会读取一次周围环境频率作为基线
 *         后面基线会自动根据环境噪声自动修正，噪声范围是指在安静环境下，
 *         noise_range=模拟信号最大值和最小值/2，也就是噪声幅度的大小，
 *         默认值为5，正常情况下不用更改，如果噪声幅度很大，可以适当提高此值。
 *按键功能;模式：1.音乐律动，2.灯光秀（8s切换一次,总共24种），3.固定当前灯光效果。
 *         按键单击则在这三种模式中切换
 *         按键长按两秒，关闭灯光，再次长按2s，则打开灯光。
 *         在灯光秀模式下关闭灯光后，再次打开，则音乐律动模式的等待效果为灯光秀的灯效，再次切换恢复默认
 *         
 *         
 */


#include <FastLED.h>
#include <WS2812FX.h>
#define NUM_LEDS 88            // LED灯珠数量
#define LED_PIN 4                // 输出控制信号引脚
#define KEY 14
#define sigPin A0     
#define Square_Led 56
#define Circle_Led 32
          
CRGB leds[NUM_LEDS];            // 建立光带leds
WS2812FX ws2812fx = WS2812FX(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

int sig;//麦克风读取数值
int sig_max=0;
int sig_max_time;
float filter_scale=1.01;//滤波系数（1.00-2.00），越小越灵敏
float BaseLine;//安静状态下的基线
int sampling=10;//采样点数
int noise_range=5;//噪声范围
int pre_sig=0;//上一次信号
int square_liup_num;//正方形区域灯点亮数
int circle_liup_num;//圆形区域灯点亮数
int pre_square_liup_num;//上一次正方形区域灯点亮数
int pre_circle_liup_num;//上一次圆形区域灯点亮数
int Square_fade;//灯回落的数量
int Circle_fade;//
int fade_num=0;

float contin_time;//无声音持续时间

int filter_max,filter_min;
uint16_t filter_data=0;
uint16_t signal_count=0;
int sig_sum=0;
float sound_mean;
int base_backup;//备份安静状态下的基线
float base_scale=1.35;//声音中位值和基线比率，超过此比率则开始基线补偿
void music_rhythm();
void loading();
uint16_t mean_filter();

void style_switch();
void fixed_effect();

int mode = 0;
unsigned char RGB_style[]={7,8,9,10,11,12,17,19,20,22,30,32,33,34,35,36,38,39,41,42,44,46,47,52};//WS2812FX比较炫酷的灯光效果
int time_count=0;
int pause_time=0;
int press_time=0;
unsigned char Led_switch=1;
unsigned long color_shift=0;
unsigned char key_value=0;
unsigned long row_count=0;
unsigned char num_=0;
unsigned char key_flag=0;
unsigned char rainbow[13][3]={
          255,0,0,//赤色
          255,165,0,//橙色
          255,255,0,//黄色
          0,255,0,//绿色
          0,127,255,//青色
          0,0,255,//蓝色
          139,0,255,//紫色
          255 ,0 ,255,//Magenta
          0,245,255,//turquoise
          0, 191, 255,//DeepSkyBlue
          60 ,179 ,113,//MediumSeaGreen
          255 ,106 ,106,//IndianRed1
          238 ,201, 0//Gold2
  };

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  pinMode(sigPin, INPUT);
  pinMode(KEY,INPUT);
  //Set all lights to make sure all are working as expected
  ws2812fx.init();
  ws2812fx.setBrightness(155);
  ws2812fx.setSpeed(100);
  ws2812fx.setSegment(0, Square_Led,NUM_LEDS,FX_MODE_RAINBOW_CYCLE,RED, 2500, false);
  ws2812fx.start();
  LEDS.addLeds<NEOPIXEL,LED_PIN>(leds, NUM_LEDS);
  FastLED.setBrightness(155);                            // 设置光带亮度
  delay(100);
  BaseLine=analogRead(sigPin);
  base_backup=BaseLine;
  loading();//开机效果
  delay(100);
  FastLED.clear();
}

void loop() {
  // put your main code here, to run repeatedly:
   switch(mode) {
    case 0://音乐律动
      music_rhythm();
      break;
    case 1:
      style_switch();//灯光秀
      break;
     case 2:
      fixed_effect();
      break;//固定灯光效果
    default:
      break;
  }
   key_value=digitalRead(KEY);//扫描按键
   if(key_value==0){
      if(millis()-press_time>2000){//长按超过2s关闭灯光
        FastLED.clear();
        FastLED.show();
        mode=0;
        Led_switch=!Led_switch;
        press_time=millis();
      }
   }
   else{
    press_time=millis();
   }
   
   if(Led_switch==0){
    mode=3;
    time_count=millis();
    Serial.println("灯光关闭");
   }
   else{
      if(key_value==0 && key_flag==1){//判断是否有按键按下
        key_flag=0;
          mode++;
          mode=mode%3;
          Serial.println("mode:"+String(mode));
          if(mode==0){//音乐律动模式
            //ws2812fx.setSegment(0, 0,NUM_LEDS,FX_MODE_RAINBOW_CYCLE,RED, 100, false);
            ws2812fx.stop();
            ws2812fx.setSegment(0, Square_Led,NUM_LEDS,FX_MODE_RAINBOW_CYCLE,RED, 2500, false);
            ws2812fx.start();
            }
           if(mode==1){
            time_count=0;
           }
        
     }
     else if(key_value==1) key_flag=1;
   }
}
void style_switch(){
   if(millis()-time_count>8000){//8秒换一种灯效
      ws2812fx.stop();
      //ws2812fx.setMode(RGB_style[num_]);
      ws2812fx.setSegment(0, 0,NUM_LEDS,RGB_style[num_],RED, 2000, false);
      num_++;
      ws2812fx.start();
      if(num_==24) num_=0;
      time_count=millis();
   }
   ws2812fx.service();
}
void fixed_effect(){
  ws2812fx.service();
}

void music_rhythm(){
  sig=mean_filter();
  
  if(sig>BaseLine*filter_scale){
    
    if(sig>sig_max){
      sig_max=sig;
    }
    if(millis()-sig_max_time>1000*60){//每隔一分钟重新计算一下信号最大值
        sig_max=0;
        sig_max_time=millis();
      }
    sig=sig>1000?1000:sig;
    Serial.println(sig);
    sig_max=constrain(sig_max,BaseLine+10,1000);
    square_liup_num=map(sig,BaseLine*filter_scale,sig_max,0,Square_Led/2);
    square_liup_num=square_liup_num>Square_Led/2 ? Square_Led/2:square_liup_num;
    circle_liup_num=map(square_liup_num,0,Square_Led/2,0,Circle_Led/2);
    if(square_liup_num>0){
      //Serial.println(square_liup_num);
      for (int i = 0; i<square_liup_num; i++){//点亮
        if(i<Square_Led/2){
          
          leds[i]=CRGB(rainbow[row_count][0],rainbow[row_count][1],rainbow[row_count][2]);
  
          leds[Square_Led/2+i]=CRGB(rainbow[row_count+1][0],rainbow[row_count+1][1],rainbow[row_count+1][2]);
       
          leds[Square_Led+map(i,0,Square_Led/2,0,Circle_Led/2)]=CRGB(rainbow[row_count][0],rainbow[row_count][1],rainbow[row_count][2]);
          leds[Square_Led+Circle_Led-map(i,0,Square_Led/2,0,Circle_Led/2)-1]=CRGB(rainbow[row_count+1][0],rainbow[row_count+1][1],rainbow[row_count+1][2]);
                
               }
        if(square_liup_num<pre_square_liup_num){//如果上一次点亮的灯珠比当前少，则在当前点亮灯珠的同时回落
          if(i<(pre_square_liup_num-square_liup_num)){
            leds [pre_square_liup_num-1-i+1]=CRGB::Black;
            leds [pre_square_liup_num-1-i]=CRGB(255 ,20 ,147);
            leds [Square_Led/2+pre_square_liup_num-1-i+1]=CRGB::Black;
            leds [Square_Led/2+pre_square_liup_num-1-i]=CRGB(0 ,0 ,128);
           }
        }
        if(circle_liup_num<pre_circle_liup_num){
          if(i<(pre_circle_liup_num-circle_liup_num)){
            leds[Square_Led+pre_circle_liup_num-1-i]=CRGB::Black;
            leds[Square_Led+pre_circle_liup_num-1-i+1]=CRGB::Black;
            leds[Square_Led+Circle_Led/2+Circle_Led/2-pre_circle_liup_num+i-1]=CRGB::Black;
            leds[Square_Led+Circle_Led/2+Circle_Led/2-pre_circle_liup_num+i]=CRGB::Black;
          }
        }
       FastLED.show();
//       delay(30-2*square_liup_num);
        delay(5);
      }
      if(pre_square_liup_num-square_liup_num>square_liup_num){//检测回落是否完成
        Square_fade=pre_square_liup_num-square_liup_num;
        pre_square_liup_num=pre_square_liup_num-square_liup_num;
      }
      else{
        Square_fade=square_liup_num;
        pre_square_liup_num=square_liup_num;
      }
      if(pre_circle_liup_num-circle_liup_num>circle_liup_num){
        Circle_fade=pre_circle_liup_num-circle_liup_num;
        pre_circle_liup_num=pre_circle_liup_num-circle_liup_num;;
      }
      else{
         Circle_fade=circle_liup_num;
         pre_circle_liup_num=circle_liup_num;
      }
      pause_time=millis();
    }
    
  }
  else{
    if(Square_fade!=0 || Circle_fade!=0){
      
        if(Square_fade>0){
          
           leds[Square_fade-1+1]=CRGB::Black;
           leds[Square_fade-1]=CRGB(255 ,20 ,147);
           leds[Square_Led/2+Square_fade-1+1]=CRGB::Black;
           leds[Square_Led/2+Square_fade-1]=CRGB(0 ,0 ,128);
           Square_fade--;
//           if(Square_fade==0){
//           leds[0]=CRGB(255 ,20 ,147);
//           leds[Square_Led/2]=CRGB(0 ,0 ,128);
//           }
        }
        if(Circle_fade>0){
          leds[Square_Led+Circle_fade-1+1]=CRGB::Black;
          leds[Square_Led+Circle_Led/2+Circle_Led/2-Circle_fade-1]=CRGB::Black;
          Circle_fade--;
        }
        pre_square_liup_num=Square_fade;
        pre_circle_liup_num=Circle_fade;
        FastLED.show();
        delay(5);
       
      }
   if(millis()-pause_time>1000){//无声音超过1s切换等待特效
        ws2812fx.service();
     }
  }
    
    if(millis()-color_shift>5000){//5s换颜色
       color_shift=millis();
       row_count++;
       if(row_count%12==0)row_count=0;
    }
  
}
void loading(){
  int remain,rand_rgb;
  remain=NUM_LEDS;
  for(int i=0;i<NUM_LEDS;i++){
    leds[NUM_LEDS-1]=CHSV(random(10, 255), 255, 200);
    for(int j=NUM_LEDS-1;j>i;j--){
      leds[j-1]=leds[j];
      leds[j]=CRGB::Black;
    }
    FastLED.show();
    delay(20);
  }
  
}

uint16_t mean_filter()//中位值平均滤波
{ int mean_singal=0,sound_sig=0;

  filter_min=analogRead(sigPin);
  for(int i=0;i<sampling;i++){
    sound_sig=analogRead(sigPin);
    filter_data=filter_data+sound_sig;
    if(sound_sig>filter_max){
      filter_max=sound_sig;
    }
    if(sound_sig<filter_min){
      filter_min=sound_sig;
    }
  }

  filter_data=filter_data-filter_max-filter_min;
  mean_singal=filter_data/(sampling-2);
  filter_data=0;
  if(mean_singal<pre_sig+noise_range && pre_sig-noise_range<mean_singal){//无声音输入持续2s更正一次基线
    if(millis()-contin_time>2000){
      BaseLine=mean_singal;
      base_backup=BaseLine;
      contin_time=millis();
    }
  }
  else{
    contin_time=millis();
  }
  if(signal_count<100){//取100次信号的平均值作为音频信号的中位值
    sig_sum=sig_sum+mean_singal;
    signal_count++;
  }
  else{
    sound_mean=sig_sum/100;
    if(sound_mean/BaseLine>base_scale){//基线补偿，防止声音过大，灯带全亮。
      BaseLine=(BaseLine+sound_mean)/2/filter_scale;
      Serial.println("基线上升至："+String(BaseLine));
    }
    else{
      Serial.println("音频中位值和基线的比为："+String(sound_mean/BaseLine)+"当前基线值："+String(BaseLine*filter_scale)+"当前声音信号值："+String(mean_singal));
      BaseLine=base_backup;
    }
    sig_sum=0;
    signal_count=0;
  }
  
  pre_sig=mean_singal;
  return mean_singal;
  
}
